package com.cnoc.base.script;

import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.springframework.stereotype.Component;

import com.cnoc.base.executor.Executor;

import groovy.lang.GroovyShell;
import groovy.lang.Script;


public class ShellScriptExecutor {
	private static final Logger log = Logger.getLogger(ShellScriptExecutor.class);

	private static final long serialVersionUID = -5988643878913597028L;

	private static ConcurrentHashMap<String, Script> cache = new ConcurrentHashMap<String, Script>();

	private static ConcurrentHashMap<String, Long> timeStampcache = new ConcurrentHashMap<String, Long>();
	
	private static ConcurrentHashMap<String, Long> badcache = new ConcurrentHashMap<String, Long>();
	
	private static final ReentrantLock lock = new ReentrantLock();
	
	public ShellScriptExecutor(){
		log.info("<GroovyShellScriptValidator> : cleaner starUp ...");
		Executor.singleton().getScheduleEcut().schedule(new Cleaner(), 86400, TimeUnit.SECONDS);
	}
	
	public Object exectute(Object object) throws Exception {
		if (object == null || !(object instanceof ShellScript)) {
			throw new IllegalArgumentException(
					"arg[object] is null or not Groovy Script object!");
		}
		Object result = null;
		Script script = null;
		ShellScript bss = null;
		try {
			bss = (ShellScript) object;

			Long s = System.currentTimeMillis();

			script = getScriptCache(bss);
				
			log.info("<GroovyShellScriptValidator> get script costs time [" + (System.currentTimeMillis() - s)+"] ms");

			bss.set(script);
			
			s = System.currentTimeMillis();
			
			result = script.run();
			
			log.info("<GroovyShellScriptValidator> run script costs time [" + (System.currentTimeMillis() - s)+"] ms");

			log.debug("<GroovyShellScriptValidator> result =" + result);
		} catch (Exception e) {
			log.error("<GroovyShellScriptValidator> run error!...",e);
			throw new Exception("groovy script["+bss.getName()+"] is invalid!", e);
		}

		return result;
	}

	private Script getScriptCache(ShellScript bss) throws Exception{
		if (bss.getScript() == null) {
			throw new IllegalArgumentException(
					"script is null,please choose one!");
		}else if (badcache.contains(bss.getScript())){
			throw new IllegalArgumentException(
					"script can not be parsed,please Correct it!");
		}
		
		Script script = cache.get(bss.getScript());
		if (null == script) {
			if(log.isDebugEnabled()){
				log.debug("<GroovyShellScriptValidator> no cache! script is :"+ bss.getScript());
			}
			try{
				lock.lock();
				log.info("<GroovyShellScriptValidator> : get a lock ...");
				script = cache.get(bss.getScript());
				if(script == null){
					Parser parser = new Parser(bss.getScript());
					Future<Script> future = Executor.singleton().getMainExecutor().submit(parser);
					script = future.get();
				}
			}catch(Exception e){
				if(bss.getScript() != null){
					badcache.put(bss.getScript(), 1L);
				}
				log.error("<GroovyShellScriptValidator> parse error!...",e);
				throw new Exception("groovy script is can not be parsed!", e);
			}finally{
				lock.unlock();
			}
		} else {
			if(log.isDebugEnabled()){
				log.debug("<GroovyShellScriptValidator> shoot cache! script is :"+ bss.getScript());
			}
		}

		Script newScript = InvokerHelper.createScript(script.getClass(), null);

		return newScript;
	}
	
	class Parser implements Callable<Script>{
		String script;
		public Parser(String script){
			this.script = script;
		}
		
		public Script call() throws Exception {
			// TODO Auto-generated method stub
			GroovyShell gs = new GroovyShell();
			Script sc = gs.parse(script);
			cache.put(script, sc);
			timeStampcache.put(script, System.currentTimeMillis());
			return sc;
		}
	}
	
	class Cleaner implements Runnable{
		public void run() {
			log.info("<GroovyShellScriptValidator> : cleaning ...");
			badcache.clear();
			Long current =  System.currentTimeMillis();
			
			Set keys = timeStampcache.keySet();
			
			Iterator it = keys.iterator();
			while(it.hasNext()){
				Object key = it.next();
				Long time = timeStampcache.get(key);
				if(current - time >= 1000*3600*24){
					log.info("<GroovyShellScriptValidator> : clean cache script...");
					cache.remove(key);
					timeStampcache.remove(key);
				}
			}
		}
	}
}
